/*******************************************************************************
 * Copyright (c) 2006 CEA List.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     CEA List - initial API and implementation
 *******************************************************************************/
package com.cea.papyrus.tabbedproperties.composites;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import org.eclipse.gef.EditPart;
import org.eclipse.gef.commands.Command;
import org.eclipse.gef.commands.CommandStack;
import org.eclipse.gef.commands.CompoundCommand;
import org.eclipse.jface.text.Assert;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.MenuItem;
import org.eclipse.swt.widgets.TableItem;
import org.eclipse.ui.views.properties.tabbed.TabbedPropertySheetPage;
import org.eclipse.uml2.uml.ClassifierTemplateParameter;
import org.eclipse.uml2.uml.ConnectableElementTemplateParameter;
import org.eclipse.uml2.uml.NamedElement;
import org.eclipse.uml2.uml.OperationTemplateParameter;
import org.eclipse.uml2.uml.TemplateSignature;
import org.eclipse.uml2.uml.TemplateableElement;

import com.cea.papyrus.core.IPapyrusUIConstants;
import com.cea.papyrus.core.PapyrusPlugin;
import com.cea.papyrus.core.commands.Di2UMLElementCreateCommand;
import com.cea.papyrus.core.commands.MoveInListCommand;
import com.cea.papyrus.core.commands.UMLElementCreateCommand;
import com.cea.papyrus.core.commands.factories.UMLDi2ElementDeleteFactory;
import com.cea.papyrus.core.editor.CommandStackUtils;
import com.cea.papyrus.core.utils.LookForElement;
import com.cea.papyrus.diagram.classdiagram.commands.Di2TemplateParameterAddCommand;
import com.cea.papyrus.diagram.classdiagram.commands.TemplateParameterReferenceCreateCommand;
import com.cea.papyrus.diagraminterchange2.di2.GraphElement;
import com.cea.papyrus.diagraminterchange2.di2.GraphNode;
import com.cea.papyrus.umlutils.Element;
import com.cea.papyrus.umlutils.TemplateParameter;

/**
 * Composite that displays the parameters of a behavior.
 */
public class TemplateParametersComposite extends DecoratedTableComposite implements ISectionComposite {

	private final static String TEMPLATE_PARAMETER_IMAGE =PapyrusPlugin.UML_ICONS_16x16+"TemplateParameter.gif";
	private final static String CLASSIFIER_TEMPLATE_PARAMETER_IMAGE =PapyrusPlugin.UML_ICONS_16x16+"ClassifierTemplateParameter.gif";
	private final static String OPERATION_TEMPLATE_PARAMETER_IMAGE =PapyrusPlugin.UML_ICONS_16x16+"OperationTemplateParameter.gif";
	private final static String CONNECTABLE_ELEMENT_TEMPLATE_PARAMETER_IMAGE =PapyrusPlugin.UML_ICONS_16x16+"ConnectableElementTemplateParameter.gif";
	
	/**
	 * key to access UML Data from table items.
	 */ 
	private final static String KEY = "parameter_data";
	
	private TabbedPropertySheetPage tabbedPropertySheetPage = null ;
	
	/**
	 * Template signature for wich template parameters are displayed.
	 */
	private GraphNode templateSignatureGraphNode;
	
	/**
	 * Template signature for wich template parameters are displayed.
	 */
	private TemplateSignature templateSignature;

	/**
	 * The templateable element owning the template signature for wich parameters are displayed
	 */
	private TemplateableElement templateableElement;
	
	/**
	 * 
	 */
	private Menu menu, menuAddReferencedTemplateParameter;
	
	/**
	 * 
	 */
	private List<MenuItem> menuItems;
	
	/**
	 * 
	 */
	private List<SelectionListener> selectionListeners;
	
	/**
	 * 
	 */
	private MenuItem templateParameterItem, 
					 classifierTemplateParameterItem,
					 operationTemplateParameterItem,
					 connectableElementTemplateParameterItem,
					 addReferencedTemplateParameterItem;
	
	/**
	 * 
	 */
	private SelectionListener templateParameterItemListener, 
							  classifierTemplateParameterItemListener, 
							  operationTemplateParameterItemListener,
							  connectableElementTemplateParameterItemListener,
							  addReferencedTemplateParameterItemListener;

	
	/**
	 * Default Constructor.
	 */
	public TemplateParametersComposite() {
		super("Template parameters");
	}

	/* (non-Javadoc)
	 * @see com.cea.papyrus.core.ui.composites.DecoratedTableComposite#createContent(org.eclipse.swt.widgets.Composite, org.eclipse.ui.views.properties.tabbed.TabbedPropertySheetWidgetFactory)
	 */
	/**
	 * 
	 * 
	 * @param factory 
	 * @param parent 
	 * 
	 * @return 
	 */
	public Composite createContent(Composite parent, TabbedPropertySheetPage tabbedPropertySheetPage) {
		Composite composite = super.createContent(parent, tabbedPropertySheetPage.getWidgetFactory());
		this.tabbedPropertySheetPage = tabbedPropertySheetPage ;
		// add an additional menu to the add Button
		createMenuForAddButton();
//		createMenuForAddReferenceButton();
		
		return composite;
	}
		
	/* (non-Javadoc)
	 * @see com.cea.papyrus.core.ui.composites.DecoratedTableComposite#setElement(com.cea.utils.Element)
	 */
	/**
	 * 
	 * 
	 * @param element 
	 */
	@Override
	public void setElement(Element element) {
		super.setElement(element);
		this.templateableElement = (TemplateableElement)element.getUml2Element() ;
		this.templateSignature = templateableElement.getOwnedTemplateSignature() ;
	}
	
	public void setGraphNode(GraphNode graphNode) {
		this.templateSignatureGraphNode = graphNode ;
	}
	
	/**
	 * Adds an additional menu to the addButton. So user can choose which element to create
	 */
	private void createMenuForAddButton() {
		menu = new Menu (addButton);
		
		// add referenced template parameter menu item
		addReferencedTemplateParameterItem = new MenuItem(menu, SWT.PUSH) ;
		addReferencedTemplateParameterItem.setText("Add referenced Template Parameter") ;
		addReferencedTemplateParameterItem.addSelectionListener(addReferencedTemplateParameterItemListener = new AddReferenceSelectionListener()) ;
		menuAddReferencedTemplateParameter = new Menu(addReferencedTemplateParameterItem) ;
		
		
		
		// add template parameter menu item
		templateParameterItem = new MenuItem (menu, SWT.PUSH);
		templateParameterItem.setText("Create owned Template Parameter");
		templateParameterItem.setImage(PapyrusPlugin.getImage(TEMPLATE_PARAMETER_IMAGE));
		templateParameterItem.addSelectionListener(templateParameterItemListener = new AddElementSelectionListener(org.eclipse.uml2.uml.TemplateParameter.class));
		
		// add classifier template parameter menu item
		classifierTemplateParameterItem = new MenuItem (menu, SWT.PUSH);
		classifierTemplateParameterItem.setText("Create owned Classifier Template Parameter");
		classifierTemplateParameterItem.setImage(PapyrusPlugin.getImage(CLASSIFIER_TEMPLATE_PARAMETER_IMAGE));
		classifierTemplateParameterItem.addSelectionListener(classifierTemplateParameterItemListener = new AddElementSelectionListener(org.eclipse.uml2.uml.ClassifierTemplateParameter.class));
		
		// add operation template parameter menu item
		operationTemplateParameterItem = new MenuItem (menu, SWT.PUSH);
		operationTemplateParameterItem.setText("Create owned Operation Template Parameter");
		operationTemplateParameterItem.setImage(PapyrusPlugin.getImage(OPERATION_TEMPLATE_PARAMETER_IMAGE));
		operationTemplateParameterItem.addSelectionListener(operationTemplateParameterItemListener = new AddElementSelectionListener(org.eclipse.uml2.uml.OperationTemplateParameter.class));
		
		// add connectable element template parameter menu item
		connectableElementTemplateParameterItem = new MenuItem (menu, SWT.PUSH);
		connectableElementTemplateParameterItem.setText("Create owned Connectable Element Template Parameter");
		connectableElementTemplateParameterItem.setImage(PapyrusPlugin.getImage(CONNECTABLE_ELEMENT_TEMPLATE_PARAMETER_IMAGE));
		connectableElementTemplateParameterItem.addSelectionListener(connectableElementTemplateParameterItemListener = new AddElementSelectionListener(org.eclipse.uml2.uml.ConnectableElementTemplateParameter.class));
		
		addButton.setMenu (menu);
	}
	
	/**
	 * Adds an additional menu to the addButton. The proposed items are the template parameters of the owning element (if any)
	 */
	private void createMenuForAddReferenceButton() {
		boolean ok = false ;
		menuAddReferencedTemplateParameter = new Menu (addButton);
		menuItems = new ArrayList<MenuItem>() ;
		selectionListeners = new ArrayList<SelectionListener>() ;
		
//		 add an item for: 
		// - each parameter (not already added) of the owner
		// - each parameter not already bound in the binding related to this templateableElement
		// - each parameter not already bound in the hierarchy of teamplateableElement (if the templateable element is a classifier)
		
		Set<org.eclipse.uml2.uml.TemplateParameter> potentialParameterReferences = new HashSet<org.eclipse.uml2.uml.TemplateParameter>() ;
		potentialParameterReferences.addAll(new com.cea.papyrus.umlutils.TemplateableElement(templateSignature.getTemplate()).getUnboundFormalParameters()) ;	
		
		// check if the owner has template parameters
		if ((templateSignature.getOwner().getOwner()) instanceof TemplateableElement) {
			if (!((TemplateableElement)templateSignature.getOwner().getOwner()).isTemplate()) {
				
			}
			else if (((TemplateableElement)templateSignature.getOwner().getOwner()).getOwnedTemplateSignature().getParameters().isEmpty()) {
				potentialParameterReferences.addAll(((TemplateableElement)templateSignature.getOwner().getOwner()).getOwnedTemplateSignature().getParameters()) ;
			}
		}
		
		if (potentialParameterReferences.isEmpty()) {
			addReferencedTemplateParameterItem.setEnabled(false) ;
			return ;
		}
		
		
		boolean enabledItem = false ;
		for (Iterator i = potentialParameterReferences.iterator() ; i.hasNext() ; ) {
			org.eclipse.uml2.uml.TemplateParameter currentFormalParameter = (org.eclipse.uml2.uml.TemplateParameter)i.next() ;
			if (!templateSignature.getParameters().contains(currentFormalParameter)) {
				enabledItem = true ;
				menuItems.add(new MenuItem(menuAddReferencedTemplateParameter, SWT.PUSH)) ;
				MenuItem item = menuItems.get(menuItems.size()-1) ;
				String templateParameterKind = "" ;
				if (currentFormalParameter instanceof ClassifierTemplateParameter) {
					item.setImage(PapyrusPlugin.getImage(CLASSIFIER_TEMPLATE_PARAMETER_IMAGE)) ;
					templateParameterKind = " (ClassifierTemplateParameter)" ;
				}
				else if (currentFormalParameter instanceof OperationTemplateParameter) {
					item.setImage(PapyrusPlugin.getImage(OPERATION_TEMPLATE_PARAMETER_IMAGE)) ;
					templateParameterKind = " (OperationTemplateParameter)" ;
				}
				else if (currentFormalParameter instanceof ConnectableElementTemplateParameter) {
					item.setImage(PapyrusPlugin.getImage(CONNECTABLE_ELEMENT_TEMPLATE_PARAMETER_IMAGE)) ;
					templateParameterKind = " (ConnectableElementTemplateParameter)" ;
				}
				else {
					item.setImage(PapyrusPlugin.getImage(TEMPLATE_PARAMETER_IMAGE)) ;
					templateParameterKind = " (TemplateParameter)" ;
				}
				if (currentFormalParameter.getParameteredElement() != null)
					item.setText(((NamedElement)currentFormalParameter.getParameteredElement()).getName() + templateParameterKind) ;
				else
					item.setText("<UNDEFINED>" + templateParameterKind) ;
				menuItems.get(menuItems.size()-1).addSelectionListener(new AddElementReferenceSelectionListener(currentFormalParameter, org.eclipse.uml2.uml.TemplateParameter.class)) ;
			}
		}
		addReferencedTemplateParameterItem.setEnabled(enabledItem) ;
	}
	
	/* (non-Javadoc)
	 * @see com.cea.papyrus.core.ui.composites.DecoratedTableComposite#refresh()
	 */
	/**
	 * 
	 */
	@Override
	public void refresh() {
		if( !table.isDisposed()&& (templateSignature!=null&& templateSignature.getOwner()!=null)){
			// Remove parameters from table 
			table.removeAll();

			// Read and add parameter
			Iterator<org.eclipse.uml2.uml.TemplateParameter> it = templateSignature.getParameters().iterator();
			while (it.hasNext()) {
				TemplateParameter templateParameter = new com.cea.papyrus.umlutils.TemplateParameter(it.next(), (TemplateableElement)templateSignature.getOwner());
				TableItem item = new TableItem(table, SWT.BORDER);
				if (templateParameter.getUml2TemplateParameter() instanceof org.eclipse.uml2.uml.ClassifierTemplateParameter)
					item.setImage(PapyrusPlugin.getImage(CLASSIFIER_TEMPLATE_PARAMETER_IMAGE)) ;
				else if (templateParameter.getUml2TemplateParameter() instanceof org.eclipse.uml2.uml.OperationTemplateParameter)
					item.setImage(PapyrusPlugin.getImage(OPERATION_TEMPLATE_PARAMETER_IMAGE)) ;
				else if (templateParameter.getUml2TemplateParameter() instanceof org.eclipse.uml2.uml.ConnectableElementTemplateParameter)
					item.setImage(PapyrusPlugin.getImage(CONNECTABLE_ELEMENT_TEMPLATE_PARAMETER_IMAGE)) ;
				else
					item.setImage(PapyrusPlugin.getImage(TEMPLATE_PARAMETER_IMAGE)) ;
				item.setData(KEY, templateParameter);
				item.setText(templateParameter.getLabel());
			}
		}
	}
	
	/* (non-Javadoc)
	 * @see com.cea.papyrus.core.ui.composites.DecoratedTableComposite#addButtonPressed()
	 */
	/**
	 * 
	 */
	@Override
	public void addButtonPressed() {
		createMenuForAddReferenceButton() ;
		menu.setVisible(true);
	}

	/* (non-Javadoc)
	 * @see com.cea.papyrus.core.ui.composites.DecoratedTableComposite#downButtonPressed()
	 */
	/**
	 * 
	 */
	@Override
	public void downButtonPressed() {
		CompoundCommand compoundCommand = new CompoundCommand();
		TableItem[] tableItems = table.getSelection();
		int[] newSelection = new int[tableItems.length] ;
		for(int i = (tableItems.length-1); i >= 0; i--) {
			// get parameters list
			List parameters = templateSignature.getOwnedParameters();
			// get parameter
			org.eclipse.uml2.uml.TemplateParameter parameter = ((TemplateParameter) tableItems[i].getData(KEY)).getUml2TemplateParameter();
			int newIndex = table.indexOf(tableItems[i]) + 1;
			if(newIndex < parameters.size()) {
				//new MoveInListCommand(parameters, parameter, newIndex).execute();
				newSelection[i] = newIndex ;
				compoundCommand.add(new MoveInListCommand(parameters, parameter, newIndex));
			}
		}
		CommandStack stack = CommandStackUtils.getCommandStack();
		Assert.isNotNull(stack, "Impossible to adapt current editor into a CommandStack");
		stack.execute(compoundCommand);
//		table.setFocus() ;
//		table.setSelection(newSelection) ;
	}

	
	
	/* (non-Javadoc)
	 * @see com.cea.papyrus.core.ui.composites.DecoratedTableComposite#editItem(org.eclipse.swt.widgets.TableItem)
	 */
	/**
	 * 
	 * 
	 * @param item 
	 */
	@Override
	public void editItem(TableItem item) {
		if (item == null) {
			return;
		}
		TemplateParameter currentTemplateParameter = (TemplateParameter)item.getData(KEY) ;
		if (currentTemplateParameter!=null) {
			EditPart selection = null ;
			for (Iterator<EditPart> i=LookForElement.getAllOutlineEditParts().iterator() ; i.hasNext() ; ) {
				EditPart current = i.next() ;
				if (current.getModel() == currentTemplateParameter.getUml2Element())
					selection = current ;
			}
			if (selection != null) {
				tabbedPropertySheetPage.selectionChanged(null, new StructuredSelection(selection)) ;
			}
		}
	}

	/* (non-Javadoc)
	 * @see com.cea.papyrus.core.ui.composites.DecoratedTableComposite#removeButtonPressed()
	 */
	/**
	 * 
	 */
	@Override
	public void removeButtonPressed() {
		CompoundCommand command = new CompoundCommand(); 
		// get selection and delete it
		TableItem[] tableItems = table.getSelection();
		for(int i = 0; i < tableItems.length ; i++) {
			// get parameter
			TemplateParameter parameter = (TemplateParameter) tableItems[i].getData(KEY);
			command.add(UMLDi2ElementDeleteFactory.eINSTANCE.createDeleteTemplateParameter(parameter.getUml2TemplateParameter(), templateSignature)) ;
		}
		CommandStack stack = CommandStackUtils.getCommandStack();
		Assert.isNotNull(stack, "Impossible to adapt current editor into a CommandStack");
		stack.execute(command);
		
	}

	/* (non-Javadoc)
	 * @see com.cea.papyrus.core.ui.composites.DecoratedTableComposite#upButtonPressed()
	 */
	/**
	 * 
	 */
	@Override
	public void upButtonPressed() {
		CompoundCommand compoundCommand = new CompoundCommand();
		TableItem[] tableItems = table.getSelection();
		List parameters = templateSignature.getOwnedParameters();
		for(int i = 0; i < tableItems.length ; i++) {
			// get parameter
			org.eclipse.uml2.uml.TemplateParameter parameter = ((TemplateParameter) tableItems[i].getData(KEY)).getUml2TemplateParameter();
			int newIndex = table.indexOf(tableItems[i]) - 1;
			if (newIndex >= 0) {
				compoundCommand.add(new MoveInListCommand(parameters, parameter, newIndex));
			}
		}
		CommandStack stack = CommandStackUtils.getCommandStack();
		Assert.isNotNull(stack, "Impossible to adapt current editor into a CommandStack");
		stack.execute(compoundCommand);
		
	}
	
	/**
	 * Listener for the menu items.
	 */
	public class AddElementReferenceSelectionListener implements SelectionListener {
		
		/**
		 * 
		 * 
		 * @param elementClass 
		 */
		public AddElementReferenceSelectionListener(org.eclipse.uml2.uml.TemplateParameter referencedParameter, Class elementClass) {
			this.elementClass = elementClass;
			this.referencedParameter = referencedParameter;
		}
		
		/**
		 * 
		 */
		public org.eclipse.uml2.uml.TemplateParameter referencedParameter;
		
		/**
		 * 
		 */
		public Class elementClass;
		
		/* (non-Javadoc)
		 * @see org.eclipse.swt.events.SelectionListener#widgetDefaultSelected(org.eclipse.swt.events.SelectionEvent)
		 */
		/**
		 * 
		 * 
		 * @param e 
		 */
		public void widgetDefaultSelected(SelectionEvent e) {
		}

		/* (non-Javadoc)
		 * @see org.eclipse.swt.events.SelectionListener#widgetSelected(org.eclipse.swt.events.SelectionEvent)
		 */
		/**
		 * 
		 * 
		 * @param e 
		 */
		public void widgetSelected(SelectionEvent e) {
			Command command ;
			CommandStack stack = CommandStackUtils.getCommandStack();
			
			if (templateSignatureGraphNode != null) {
				command = new Di2TemplateParameterAddCommand(elementClass, referencedParameter, templateSignature, (GraphElement)templateSignatureGraphNode.getContained().get(0), IPapyrusUIConstants.CLASS_DIAGRAM) ;
			}
			else {
				command = new TemplateParameterReferenceCreateCommand(elementClass , templateSignature, referencedParameter) ;
			}
			Assert.isNotNull(stack, "Impossible to adapt current editor into a CommandStack");
			stack.execute(command);
			refresh() ;
		}
	}
	
	/**
	 * Listener for the menu items.
	 */
	public class AddReferenceSelectionListener implements SelectionListener {
		
		
		/* (non-Javadoc)
		 * @see org.eclipse.swt.events.SelectionListener#widgetDefaultSelected(org.eclipse.swt.events.SelectionEvent)
		 */
		/**
		 * 
		 * 
		 * @param e 
		 */
		public void widgetDefaultSelected(SelectionEvent e) {
		}

		/* (non-Javadoc)
		 * @see org.eclipse.swt.events.SelectionListener#widgetSelected(org.eclipse.swt.events.SelectionEvent)
		 */
		/**
		 * 
		 * 
		 * @param e 
		 */
		public void widgetSelected(SelectionEvent e) {
			Command command ;
			CommandStack stack = CommandStackUtils.getCommandStack();
			menuAddReferencedTemplateParameter.setVisible(true) ;
		}
	}
	
	/**
	 * Listener for the menu items.
	 */
	public class AddElementSelectionListener implements SelectionListener {
		
		/**
		 * 
		 * 
		 * @param elementClass 
		 */
		public AddElementSelectionListener(Class elementClass) {
			this.elementClass = elementClass;
		}
		
		/**
		 * 
		 */
		public Class elementClass;
		/* (non-Javadoc)
		 * @see org.eclipse.swt.events.SelectionListener#widgetDefaultSelected(org.eclipse.swt.events.SelectionEvent)
		 */
		/**
		 * 
		 * 
		 * @param e 
		 */
		public void widgetDefaultSelected(SelectionEvent e) {
		}

		/* (non-Javadoc)
		 * @see org.eclipse.swt.events.SelectionListener#widgetSelected(org.eclipse.swt.events.SelectionEvent)
		 */
		/**
		 * 
		 * 
		 * @param e 
		 */
		public void widgetSelected(SelectionEvent e) {
			Command command ;
			CommandStack stack = CommandStackUtils.getCommandStack();
			
			if (templateSignatureGraphNode != null) {
				command = new Di2UMLElementCreateCommand(elementClass, (GraphElement)templateSignatureGraphNode.getContained().get(0),IPapyrusUIConstants.CLASS_DIAGRAM) ;
			}
			else {
				command = new UMLElementCreateCommand(elementClass , templateSignature) ;
			}
			Assert.isNotNull(stack, "Impossible to adapt current editor into a CommandStack");
			stack.execute(command);
			refresh() ;
		}
	}
}
